# -*- coding: utf-8 -*-

from __future__ import division

import re
import math
import random
import datetime
import calendar
from contextlib import suppress

import six


MINYEAR = datetime.MINYEAR
MAXYEAR = datetime.MAXYEAR

MINDATE = datetime.date.min
MAXDATE = datetime.date.max

MINDATETIME = datetime.datetime.min
MAXDATETIME = datetime.datetime.max

DateTime = datetime.datetime
Date = datetime.date
Time = datetime.time
TimeDelta = datetime.timedelta

today = datetime.date.today
now = datetime.datetime.now

fromtimestamp = datetime.datetime.fromtimestamp

A_DAY = TimeDelta(days=1)
A_WEEK = TimeDelta(days=7)
A_MONTH = TimeDelta(days=30)
A_YEAR = TimeDelta(days=365)

CENTENARY = TimeDelta(days=36500)

day_before_yesterday = lambda: today() - (A_DAY * 2)
yesterday = lambda: today() - A_DAY
tomorrow = lambda: today() + A_DAY
day_after_tomorrow = lambda: today() + (A_DAY * 2)

combine_datetime = datetime.datetime.combine


def last_day_of_month(date=None):
    if not date:
        date = today()
    month_max_day = calendar.monthrange(date.year, date.month)[1]
    return datetime.date(year=date.year, month=date.month, day=month_max_day)


def is_last_day_of_month(date=None):
    if not date:
        date = today()
    return date.day == calendar.monthrange(date.year, date.month)[1]


def is_date_type(value):
    return isinstance(value, (Date, DateTime))


def date2dt(value):
    """Convert datetime.date to datetime.datetime"""
    return combine_datetime(value, Time.min)


def to_date(value):
    """Convert anything datetime.date"""
    if isinstance(value, six.string_types):
        if ':' in value:
            value = value[:10]
        with suppress(ValueError):
            return Date(*map(int, value.split('-')))
    elif isinstance(value, datetime.datetime):
        return value.date()
    elif isinstance(value, datetime.date):
        return value
    raise ValueError("need a datetime.date, datetime.datetime or like"
                     "'2020-02-02', Got '{!r}'".format(value))


def to_datetime(value):
    """Convert anything to datetime.datetime"""
    if isinstance(value, (str, six.text_type)):
        with suppress(ValueError):
            return DateTime(*map(int, re.split(r"\W+", value)))
    elif isinstance(value, DateTime):
        return value
    elif isinstance(value, Date):
        return date2dt(value)
    raise ValueError("need a datetime.date, datetime.datetime or like "
                     "'2015-01-05 12:00:00', Got '{!r}'".format(value))


def to_timestamp(value):
    """Convert anything to timestamp"""
    dt = to_datetime(value)
    return dt.timestamp()


def parse_date(value):
    """Parse other date format to datetime.date"""
    if isinstance(value, datetime.datetime):
        return value.date()
    if isinstance(value, datetime.date):
        return value
    if isinstance(value, six.string_types):
        if '-' in value:
            return datetime.datetime.strptime(value, "%Y-%m-%d").date()
        else:
            return datetime.datetime.strptime(value, "%Y%m%d").date()
    if isinstance(value, six.integer_types):
        return datetime.date(year=value // 10000,
                             month=(value // 100) % 100,
                             day=value % 100)
    raise ValueError("Unknown {} for parse_date.".format(value))


def date2str(date, separator=None):
    """Convert datetime.date to string type"""
    if not isinstance(date, Date):
        raise ValueError("date must be datetime.date")
    separator = separator if separator else ""
    style = separator.join(["%Y", "%m", "%d"])
    return date.strftime(style)


def ever(minimum=None, maximum=None):
    """Get a random datetime"""
    minimum = to_datetime(minimum) if minimum else now() - CENTENARY
    maximum = to_datetime(maximum) if maximum else now() + CENTENARY
    assert minimum < maximum

    return fromtimestamp(random.randint(
        minimum.timestamp() * 1000000,
        maximum.timestamp() * 1000000
    ) / 1000000.0)


class Quarter(object):
    """Quarter type"""

    def __init__(self, fp):
        if isinstance(fp, six.string_types) and 'q' in fp:
            self.year, self.q = [int(i) for i in fp.split("q")]
        elif isinstance(fp, (tuple, list)) and len(fp) == 2:
            self.year, self.q = [int(i) for i in fp]
        elif isinstance(fp, int):
            self.year, self.q = self.__convert(fp)
        elif isinstance(fp, (Date, DateTime)):
            self.year, self.q = fp.year, int(math.ceil(fp.month / 3.0))
        else:
            raise ValueError("param error '%s'" % fp)

    @property
    def _qs(self):
        return self.year * 4 + self.q

    @staticmethod
    def __convert(qs):
        _y, _q = divmod(qs, 4)
        if _q == 0:
            _y -= 1
            _q = 4
        return _y, _q

    def __add__(self, step):
        return Quarter(self.__convert(self._qs + step))

    def __sub__(self, step):
        return Quarter(self.__convert(self._qs - step))

    def __repr__(self):
        return "Quarter({}, {})".format(self.year, self.q)

    def __str__(self):
        return "{}q{}".format(self.year, self.q)
